package com.deepclassifieds.daas.api;

import java.io.Serializable;

import com.deepclassifieds.daas.common.Reject;

/**
 * Daas Unique Identifier of record.<p>
 * Virtual shard number is encoded into final value.
 * @author nbarkhatov
 * @since 1.0
 */
public final class DaasUID implements Serializable {

	private static final long serialVersionUID = 20110221123402L;

	/** Using right bits to encode virtual shard number into id to make it UID */
	static final int  SHARD_NUMBER_BITS = Integer.parseInt(System.getProperty("daas.shard.number.bits", "10")); // 2^10 == 1024 max shards [0 - 1023]
	/** Precomputed shard mask */
	static final long SHARD_MASK; // 10 bits = ...0001111111111
	/** Maximum virtual shard number */
	static final int  SHARD_MAX; // 10 bits
	/** Maximum Id number within virtual shard */
	static final long ID_MAX; // 53 bits
	/** Maximum number if digits */
	public static final int SHARD_NUMBER_MAX_DIGITS;
	static {
		long shardMask = 0L;
		for(int i = 0; i < SHARD_NUMBER_BITS; ++i, shardMask <<= 1, shardMask ^= 1L);
		SHARD_MASK = shardMask;
		SHARD_MAX = (int)shardMask;
		long idMax = -1L; // set all bits
		idMax ^= shardMask;
		idMax >>>= (SHARD_NUMBER_BITS + 1);
		ID_MAX = idMax;
		SHARD_NUMBER_MAX_DIGITS = Integer.toString(SHARD_MAX).length();
	}
	
	private final long value;
	
	private final int shardNumber;
	private final long id;
	
	/**
	 * Constructor.
	 * @param shardNumber Virtual shard number.
	 * @param id Unique record Id within virtual shard.
	 */
	public DaasUID(int shardNumber, long id) {
		Reject.ifFalse(shardNumber >= 0);
		Reject.ifFalse(shardNumber <= SHARD_MAX);
		Reject.ifFalse(id >= 0);
		Reject.ifFalse(id <= ID_MAX);
		this.shardNumber = shardNumber;
		this.id = id;
		this.value = (id << SHARD_NUMBER_BITS) | ((long) shardNumber);
	}

	public long getValue() {
		return value;
	}

	public int getShardNumber() {
		return shardNumber;
	}

	public long getId() {
		return id;
	}
	
	public static DaasUID valueOf(long value) {
		int shard = (int)(value & SHARD_MASK);
		long id = value >> SHARD_NUMBER_BITS;
		return new DaasUID(shard, id);
	}

	public static DaasUID valueOf(String value) {
		return valueOf(Long.parseLong(value));
	}

	@Override
	public String toString() {
		return Long.toString(value);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (value ^ (value >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) return true;
		if (obj == null) return false;
		if (getClass() != obj.getClass()) return false;
		DaasUID other = (DaasUID) obj;
		if (value != other.value) return false;
		return true;
	}

}
